/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.core; import java.io.*; import java.beans.*; import java.util.*; import org.openide.ServiceType; import org.openide.modules.ManifestSection; import org.openide.nodes.*; import org.openide.util.enum.*; import org.openide.util.Mutex; import org.openide.util.io.NbMarshalledObject; import org.netbeans.beaninfo.editors.ExecutorEditor; import org.netbeans.beaninfo.editors.CompilerTypeEditor; import org.netbeans.beaninfo.editors.DebuggerTypeEditor; /** Work with all service types. * * @author Jaroslav Tulach */ final class Services extends ServiceType.Registry { /** serial */ static final long serialVersionUID =-7558069607307508327L; /** instance */ private static final Services INSTANCE = new Services (); /** first level to use */ public static final SubLevel FIRST = new SubLevel (); /** current list of all services */ private static List current; /** Default instance */ public static Services getDefault () { return INSTANCE; } /** Override to specially look up no-op services. */ public ServiceType find (Class clazz) { if (clazz == ExecutorEditor.NoExecutor.class) return ExecutorEditor.NO_EXECUTOR; else if (clazz == CompilerTypeEditor.NoCompiler.class) return CompilerTypeEditor.NO_COMPILER; else if (clazz == DebuggerTypeEditor.NoDebugger.class) return DebuggerTypeEditor.NO_DEBUGGER; else return super.find (clazz); } /** Override to specially look up no-op services. */ public ServiceType find (String name) { if (name.equals (ExecutorEditor.NO_EXECUTOR.getName ())) return ExecutorEditor.NO_EXECUTOR; else if (name.equals (CompilerTypeEditor.NO_COMPILER.getName ())) return CompilerTypeEditor.NO_COMPILER; else if (name.equals (DebuggerTypeEditor.NO_DEBUGGER.getName ())) return DebuggerTypeEditor.NO_DEBUGGER; else return super.find (name); } /** Adds new section. */ public static void addService (final ManifestSection.ServiceSection s) throws InstantiationException { try { Children.MUTEX.writeAccess (new Mutex.ExceptionAction () { public Object run () throws InstantiationException { FIRST.add (s); return null; } }); } catch (org.openide.util.MutexException ex) { throw (InstantiationException)ex.getException (); } } /** Removes a section. */ public static void removeService (final ManifestSection.ServiceSection s) throws InstantiationException { try { Children.MUTEX.writeAccess (new Mutex.ExceptionAction () { public Object run () throws InstantiationException { FIRST.remove (s); return null; } }); } catch (org.openide.util.MutexException ex) { throw (InstantiationException)ex.getException (); } } /** fires property change. */ static void firePropertyChange () { current = null; } /** Debugging? */ static boolean debug () { return System.getProperty ("netbeans.debug.exceptions") != null; } /** Getter for list of all services types. * @return list of ServiceType */ private java.util.List getServiceTypesImpl () { List l = current; if (l == null) { l = (List)Children.MUTEX.readAccess (new Mutex.Action () { public Object run () { LinkedList ll = new LinkedList (); Enumeration en = FIRST.services (); while (en.hasMoreElements ()) { ll.add (en.nextElement ()); } return ll; } }); current = l; } return l; } /** Getter for list of all services types. * @return list of ServiceType */ public java.util.List getServiceTypes () { return new LinkedList (getServiceTypesImpl ()); } /** Setter for list of all services types. This allows to change * instaces of the objects but only of the types that are already registered * to the system by manifest sections. * * @param arr list of ServiceTypes */ private void setServiceTypesImpl (final java.util.List arr) { Children.MUTEX.postWriteRequest (new Runnable () { public void run () { current = null; FIRST.changeAll (arr); firePropertyChange (); } }); } /** Setter for list of all services types. This allows to change * instaces of the objects but only of the types that are already registered * to the system by manifest sections. * * @param arr list of ServiceTypes */ public void setServiceTypes (java.util.List arr) { setServiceTypesImpl (new LinkedList (arr)); } /** all services */ public Enumeration services () { return Collections.enumeration (getServiceTypesImpl ()); } /** Write the object down. */ private void writeObject (ObjectOutputStream oos) throws IOException { Enumeration en = services (); while (en.hasMoreElements ()) { ServiceType s = (ServiceType)en.nextElement (); NbMarshalledObject obj; try { obj = new NbMarshalledObject (s); } catch (IOException ex) { if (debug ()) ex.printStackTrace(); // skip the object if it cannot be serialized obj = null; } if (obj != null) { oos.writeObject (obj); } } oos.writeObject (null); } /** Read the object. */ private void readObject (ObjectInputStream oos) throws IOException, ClassNotFoundException { final LinkedList ll = new LinkedList (); for (;;) { NbMarshalledObject obj = (NbMarshalledObject)oos.readObject (); if (obj == null) { break; } try { ServiceType s = (ServiceType)obj.get (); ll.add (s); } catch (IOException ex) { if (debug ()) ex.printStackTrace(); } catch (ClassNotFoundException ex) { if (debug ()) ex.printStackTrace(); } } setServiceTypesImpl (ll); } /** Only one instance */ private Object readResolve () { return INSTANCE; } /** Interface that has to be implemented by all children displaying the * services. */ static interface Level { /** Implements the addition of a section into the container. */ public void add (ManifestSection.ServiceSection section) throws InstantiationException; /** Removes the section for levels. * @return true if the level is now empty and should be deleted */ public boolean remove (ManifestSection.ServiceSection section) throws InstantiationException; /** Method to obtain all ServiceType(s) presented in this level and * in all sublevels. * * @return enumeration of ServiceType */ public Enumeration services (); /** Takes collection of new services and replaces its content * by them. The replaces services should be deleted from the * set. * * @param c collection of ServiceTypes */ public void changeAll (Collection c); } /** Special children for handling of ManifestSection.ServiceSection(s). */ static abstract class SectionChildren extends Index.ArrayChildren implements Level { /** map from (Object, Node) */ private java.util.Map map; /** Use linked list */ protected java.util.Collection initCollection () { return new LinkedList (); } /** Implements the addition of a section into the container. */ public final void add (ManifestSection.ServiceSection section) throws InstantiationException { Object key = key (section.getServiceType()); if (map == null) { map = new HashMap (11); } if (nodes == null) { nodes = initCollection (); } Node subNode = (Node)map.get (key); if (subNode == null) { subNode = createChildren (section); map.put (key, subNode); // move as first if the section is marked as default if (section.isDefault ()) { ((LinkedList)nodes).addFirst (subNode); } else { nodes.add (subNode); } refresh (); } Level l = (Level)subNode.getChildren (); l.add (section); } /** Removes a section from this level and all sublevels. */ public final boolean remove (ManifestSection.ServiceSection section) throws InstantiationException { Object key = key (section.getServiceType()); Node subNode = (Node)map.get (key); Level l = (Level)subNode.getChildren (); if (l.remove (section)) { map.remove (key); nodes.remove (subNode); refresh (); } return getNodesCount () == 0; } /** Reorder of children. */ public void reorder (int[] perm) { super.reorder (perm); firePropertyChange (); } /** Method to obtain all ServiceType(s) presented in this level and * in all sublevels. * * @return enumeration of ServiceType */ public final Enumeration services () { Enumeration en = nodes (); // enumeration of enumerations AlterEnumeration aen = new AlterEnumeration (en) { public Object alter (Object o) { Node n = (Node)o; return ((Level)n.getChildren ()).services (); } }; // concatenate enumerations return new SequenceEnumeration (aen); } /** Takes collection of new services and replaces its content * by them. The replaces services should be deleted from the * set. * * @param c collection of ServiceTypes */ public void changeAll (Collection c) { java.util.Map unusedEntries = (java.util.Map) ((java.util.HashMap) map).clone(); Iterator it = c.iterator (); LinkedList newKeys = new LinkedList (); while (it.hasNext ()) { ServiceType s = (ServiceType)it.next (); // if the key is not there yet try { Object key = key(s); Node n = (Node)map.get(key); if (n != null && !newKeys.contains (n)) { newKeys.add (n); unusedEntries.remove(key); } } catch (InstantiationException e) { } } // and vice versa - remove non used entries HashSet ignore = new HashSet(); it = unusedEntries.entrySet().iterator(); if (it.hasNext()) { while (it.hasNext()) { java.util.Map.Entry entry = (java.util.Map.Entry) it.next(); Class clz = (Class) entry.getKey(); if (this.getClass () != TypeLevel.class) { map.remove(entry.getKey()); nodes.remove((Node) entry.getValue()); } else if (clz.getName().indexOf("FastJavacCompilerType") >= 0) { // NOI18N [TODO] ignore.add(entry.getValue()); } } } // refresh the nodes because they could be updated by // previous loop refresh (); // now compute the permutation to be applied Node[] current = getNodes (); int[] perm = new int[current.length]; int max = newKeys.size(); for (int i = 0; i < current.length; i++) { int indx = newKeys.indexOf (current[i]); if (indx == -1) { // node is not present => add at the end perm[i] = max++; } else { // node present => do the right position perm[i] = indx; } } Enumeration en = nodes (); while (en.hasMoreElements ()) { Node n = (Node)en.nextElement (); Level l = (Level)n.getChildren (); if (ignore.contains(n)) { l.changeAll(Collections.EMPTY_LIST); } else { l.changeAll (c); } } // reorder without firing super.reorder (perm); } /** Computes a key for given section. This key is used * as an index into the map. * * @param service seciton * @return key to use */ protected abstract Object key (ServiceType service) throws InstantiationException; /** Creates a subnode with children for given * section. * * @param section section to create the children for * @return node with empty children implementing Level */ protected abstract Node createChildren (ManifestSection.ServiceSection section) throws InstantiationException; } /** Children that will display only direct subclasses of ServiceType. */ static final class SubLevel extends SectionChildren { /** Computes a key for given section. This key is used * as an index into the map. * * @param section seciton * @return key to use */ protected Object key (ServiceType section) throws InstantiationException { return findClass (section); } /** Creates a subnode with children for given * section. * * @param section section to create the children for * @return node with empty children implementing Level */ protected Node createChildren (ManifestSection.ServiceSection section) throws InstantiationException { AbstractNode an = new ServicesNode.SubLevel ( findClass (section.getServiceType ()) ); return an; } /** Finds the right class for given seciton */ private static Class findClass (ServiceType s) throws InstantiationException { Class c = s.getClass (); for (;;) { Class ss = c.getSuperclass (); if (ss == ServiceType.class) { return c; } c = ss; } } } /** Children that sorts object by their sections. */ static final class TypeLevel extends SectionChildren { /** Computes a key for given section. This key is used * as an index into the map. * * @param section seciton * @return key to use */ protected Object key (ServiceType section) throws InstantiationException { return section.getClass (); } /** Creates a subnode with children for given * section. * * @param section section to create the children for * @return node with empty children implementing Level */ protected Node createChildren (ManifestSection.ServiceSection section) throws InstantiationException { AbstractNode an = new ServicesNode.TypeLevel ( section.getServiceType ().getClass () ); return an; } } /** Children that will hold default instance and all other instances * of the same class. */ static final class InstanceLevel extends Children.Keys implements Level { /** the section */ private ManifestSection.ServiceSection section; /** all copied executors */ private List all; /** current default, or null */ private ServiceType def; public InstanceLevel () { setBefore (true); all = new LinkedList (); def = null; } /** How to create new node for a ServiceType */ protected Node[] createNodes (Object key) { try { return new Node[] { new ServicesNode.InstanceLevel ((ServiceType)key) }; } catch (IntrospectionException ex) { if (debug ()) ex.printStackTrace(); return new Node[0]; } } /** Implements the addition of a section into the container. */ public void add(ManifestSection.ServiceSection section) throws InstantiationException { ServiceType s = section.getServiceType (); //!!!!!!!!! more executors of the same class if (section.isDefault () || this.section == null) { this.section = section; def = s; } //System.out.println("Into: " + this + " adding: " +s.getName ()); // NOI18N try { add (s); } catch (Exception ex) { if (debug ()) ex.printStackTrace(); return; } if (section.isDefault () && def != null) { refreshKey (def); } } /** Removes the section for levels. */ public boolean remove(ManifestSection.ServiceSection section) throws InstantiationException { ServiceType s = section.getServiceType (); destroy (s); if (def == null) { return true; } refreshKey (def); return false; } /** Method to obtain all ServiceType(s) presented in this level and * in all sublevels. * * @return enumeration of ServiceType */ public Enumeration services() { return Collections.enumeration (all); } // // Modification of content // /** Creates new instance. */ void create () throws Exception { add (uniquify (section.createServiceType ())); } /** Test whether the services repository contains the supplied name. */ private static boolean containsName (String name) { Enumeration e = INSTANCE.services (); while (e.hasMoreElements ()) { ServiceType s = (ServiceType) e.nextElement (); if (s.getName ().equals (name)) return true; } return false; } /** If this service type will have a unique name, return it; else create a copy with a new unique name. */ static ServiceType uniquify (ServiceType type) throws IOException, ClassNotFoundException { if (containsName (type.getName ())) { type = (ServiceType) new NbMarshalledObject (type).get (); String name = type.getName (); int suffix = 2; String newname; while (containsName (newname = Main.getString ("LBL_ServiceType_Duplicate", name, String.valueOf (suffix)))) suffix++; type.setName (newname); } return type; } /** Adds new instance or copy of the instance. */ public void add (ServiceType s) throws Exception { if (all.contains (s)) { NbMarshalledObject m = new NbMarshalledObject (s); s = (ServiceType)m.get (); } if (all == Collections.EMPTY_LIST) { throw new RuntimeException(Main.getString("EXC_Forbidden_New")); } all.add (s); def = (ServiceType) all.get (0); setKeys (all); firePropertyChange (); } /** Destroys the service */ public void destroy (ServiceType s) { all.remove (s); def = all.isEmpty () ? null : (ServiceType) all.get (0); setKeys (all); firePropertyChange (); } /** Takes collection of new services and replaces its content * by them. The replaces services should be deleted from the * set. * * @param c collection of ServiceTypes */ public void changeAll (Collection c) { Class cl; try { cl = section.getServiceType ().getClass (); } catch (InstantiationException ex) { if (debug ()) ex.printStackTrace(); return; } List ll = new LinkedList (); Iterator it = c.iterator (); while (it.hasNext ()) { ServiceType s = (ServiceType) it.next (); // This is apparently to be expected: if (s.getClass () != cl) continue; // Weird but I think necessary... --jglick it.remove (); ll.add (s); } // Again weird but errors otherwise... --jglick if (!ll.isEmpty ()) { // update current state def = (ServiceType) ll.get (0); setKeys (all = ll); it = all.iterator (); while (it.hasNext ()) refreshKey (it.next ()); firePropertyChange (); } // hack for FastJavacCompilerType - present in a manifest but removed later if (c == Collections.EMPTY_LIST) { setKeys(all = Collections.EMPTY_LIST); firePropertyChange (); } } /** Get an index support capable of rearranging the instances. */ public Index getIndex () { return new Index.Support () { public Node[] getNodes () { return InstanceLevel.this.getNodes (); } public int getNodesCount () { return getNodes ().length; } public void reorder (int[] perm) { ServiceType[] nue = new ServiceType[perm.length]; for (int i = 0; i < perm.length; i++) nue[i] = ((ServicesNode.InstanceLevel) getNodes () [perm[i]]).getService (); changeAll (new ArrayList (Arrays.asList (nue))); } }; } } } /* * Log * 18 Gandalf-post-FCS1.16.3.0 3/24/00 Ales Novak setServiceTypes fixed * 17 Gandalf 1.16 1/14/00 Martin Ryzl bug in setting new * services fixed * 16 Gandalf 1.15 1/13/00 Jaroslav Tulach I18N * 15 Gandalf 1.14 12/21/99 Jaroslav Tulach serviceTypes r/w property * 14 Gandalf 1.13 12/20/99 Jesse Glick No more "default * services", all are freely reorderable. * 13 Gandalf 1.12 11/16/99 Ales Novak RuntimeException Platform * catched * 12 Gandalf 1.11 11/10/99 Ales Novak InstanceLevel.add - * default is properly handled * 11 Gandalf 1.10 10/29/99 Jesse Glick Added "(no compiler)" * etc. to service type selection panel. * 10 Gandalf 1.9 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 9 Gandalf 1.8 10/15/99 Jaroslav Tulach Enterprise debugger can * be autoupdated. * 8 Gandalf 1.7 10/5/99 Jaroslav Tulach Handles missing sections * too. * 7 Gandalf 1.6 10/5/99 Jaroslav Tulach Small improvement. * 6 Gandalf 1.5 10/4/99 Jesse Glick Make Default action on * service types. * 5 Gandalf 1.4 10/1/99 Jesse Glick Cleanup of service type * name presentation. * 4 Gandalf 1.3 9/21/99 Jaroslav Tulach Updates the list of * services when reorder is performed. * 3 Gandalf 1.2 9/19/99 Jaroslav Tulach Read/write external * remembers order of services. * 2 Gandalf 1.1 9/17/99 Jaroslav Tulach Reorder of nodes works. * 1 Gandalf 1.0 9/10/99 Jaroslav Tulach * $ */